/*
	klib_system_resources.c

	provides functions to view system resource allocation and usage

	Author: Aidan Goddard 23/10/13
*/

#include"headers/klib_system_resources.h"
#include"../kernel/headers/scheduler_functions.h"
#include"../kernel/headers/scheduler_tables.h"
#include"../kernel/headers/system_config_tables.h"

uint32_t	__KLIB__RCGetCPUUsage(uint32_t CPU)
{
	return GetCPUUsage(CPU);
}

uint32_t	__KLIB__RCGetCPUActiveThreads(uint32_t CPU)
{
	return GetCPUActiveThreads(CPU);
}

uint64_t	__KLIB__RCGetTID()
{
	__asm__ __volatile__("cli":::"memory");
	uint64_t to_return = GetCurrentTID();
	__asm__ __volatile__("sti":::"memory");
	return to_return;
}

uint64_t	__KLIB__RCGetPID()
{
	__asm__ __volatile__("cli":::"memory");
	uint64_t to_return = GetCurrentTID();
	to_return = to_return >> 8;
	__asm__ __volatile__("sti":::"memory");
	return to_return;
}

void		__KLIB__RCGetProcs(uint64_t *proc_list, uint64_t *proc_count)
{
	// need to get the PIDs of all active processes
	// first get count of active processes
	uint32_t p_count = SCHEDULER->total_process_count + 1;

	// get each PID
	SCH_PCB *pcb = (SCH_PCB*)(SCHEDULER->PCB);
	int i, k;
	for(i = 0, k = 0; i < 2048 && k < p_count; i++)
	{
		if(pcb[i].state != 0)
		{
			proc_list[k++] = pcb[i].PID;
		}
	}

	*proc_count = k;

	return;
}

void		__KLIB__RCGetProcDetails(uint64_t PID, uint32_t *state, uint32_t *owner_id)
{
	// get pointer to the PCB
	SCH_PCB *pcb = (SCH_PCB*)(&(SCHEDULER->PCB[PID & 0xffff]));

	// check PID is valid
	if(pcb->PID == PID)
	{
		*state = pcb->state;
		*owner_id = pcb->owner_ID;
	}
	else
	{
		*state = 0;
		*owner_id = 0;
	}

	// finished
	return;
}

void 		__KLIB__RCGetProcImageName(uint64_t PID, char *dest_buffer, int *length)
{
	// get pointer to the PCB
	SCH_PCB *pcb = (SCH_PCB*)(&(SCHEDULER->PCB[PID & 0xffff]));

	// check PID is valid
	if(pcb->PID == PID)
	{
		// get ptr to name start
		uint8_t *src_ptr = (uint8_t*)(pcb->PDB->image_name);
		//uint8_t *src_ptr = (uint8_t*)(pcb->process_description);
		uint8_t *dest_ptr = (uint8_t*)dest_buffer;

		// copy
		int i;
		for(i = 0; i < 256; i++)
		{
			dest_ptr[i] = src_ptr[i];
			if(dest_ptr[i] == 0)
			{
				*length = i + 1;
				break;
			}
		}
	}
	else
	{
		dest_buffer[0] = 0;
	}

	// finished
	return;
}


void __KLIB__RCGetProcDescription(uint64_t PID, char *dest_buffer, uint64_t *length)
{
	// get pointer to the PCB
	SCH_PCB *pcb = (SCH_PCB*)(&(SCHEDULER->PCB[PID & 0xffff]));

	// check PID is valid
	if(pcb->PID == PID)
	{
		// get ptr to name start
		//uint64_t *src_ptr = (uint64_t*)(pcb->PDB->image_name);
		uint8_t *src_ptr = (uint8_t*)(pcb->process_description);
		uint8_t *dest_ptr = (uint8_t*)dest_buffer;

		// copy
		int i;
		for(i = 0; i < 256; i++)
		{
			dest_ptr[i] = src_ptr[i];
			if(dest_ptr[i] == 0)
			{
				*length = i + 1;
				break;
			}
		}
	}
	else
	{
		dest_buffer[0] = 0;
	}

	// finished
	return;
}

// returns a list of 64 bit values which indicate the thread number and its state
// where value = (thread_number << 24) + thread_usage
void __KLIB__RCGetThreads(uint64_t PID, uint64_t *thread_list, uint64_t *thread_count)
{
	// get pointer to the PCB
	SCH_PCB *pcb = (SCH_PCB*)(&(SCHEDULER->PCB[PID & 0xffff]));

	// check PID is valid
	if(pcb->PID == PID)
	{
		// get pointer to the threads table for the proc
		uint32_t *thread_usage = (uint32_t*)(pcb->PDB->thread_usage_map);

		int active_threads = pcb->active_threads;
		int max_threads = pcb->max_threads_in_TCB;
		int i, k;
		for(i = 0, k = 0; k < active_threads && i < max_threads; i++)
		{
			if(thread_usage[i] != THREAD_STATE_INACTIVE)
			{
				thread_list[k++] = (i << 24) + thread_usage[i];//(PID << 8) + i;
			}
		}

		*thread_count = active_threads;
	}
	else
	{
		*thread_count = 0;
	}

	return;
}

// returns the various memory values for the PID
void __KLIB__RCGetProcMemory(uint64_t PID, uint64_t *PML4, uint64_t *pages_allocated, uint64_t *kernel_memory_allocated)
{
	// get pointer to the PCB
	SCH_PCB *pcb = (SCH_PCB*)(&(SCHEDULER->PCB[PID & 0xffff]));

	// check PID is valid
	if(pcb->PID == PID)
	{
		*PML4 = pcb->PML4;

		// TODO: fill in pages allocated

		// TODO: fill in kernel memory allocated

	}
	else
	{
		*PML4 = 0;
		*pages_allocated = 0;
		*kernel_memory_allocated = 0;
	}

	// finished
	return;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// system date/time stuff

// function to get the milliseconds since epoc
static uint64_t GetSystemMillis()
{
	TBL_SYSCONFIG *tbl = GetSystemConfigBase();
	return tbl->tbl_stuff.time;
}

// function to get the number of full days since the epoc and the remaining ms
static void GetDaysSinceEpoc(uint32_t *days, uint32_t *millis)
{
	*days = (uint32_t)(GetSystemMillis() / (24 * 60 * 60 * 1000));
	*millis = (uint32_t)(GetSystemMillis() % (24 * 60 * 60 * 1000));
	return;
}

// function to get the number of years passed since the epoc and the remaining days in the year
static void GetYearsSinceEpoc(uint32_t *years, uint32_t *days, uint32_t *millis)
{
	// get days and milliseconds since epoc
	uint32_t i_days = 0;
	GetDaysSinceEpoc(&i_days, millis);

	// have days since epoc, need to work out how many years
	uint32_t i_years = 0;
	int y = 1970;
	while(i_days >= 365)
	{
		// check if leap year or not
		if((y % 4 == 0) && (y != 2100))
		{
			// is a leap year
			i_days -= 366;
		}
		else
		{
			// is not a leap year
			i_days -= 365;
		}
		y++;
		i_years++;
	}

	// return years and days
	*years = i_years;
	*days = i_days;
	return;

}

uint32_t	__KLIB__SystemTimeGetYear(void)
{
	// get years passed since epoc
	uint32_t years, days, millis;
	GetYearsSinceEpoc(&years, &days, &millis);
	return years + 1970;
}

// array giving days in each of the months for a standard year
static uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

uint32_t	__KLIB__SystemTimeGetMonth(void)
{
	// get remaining days of year
	uint32_t years, days, millis;
	GetYearsSinceEpoc(&years, &days, &millis);
	days++;	// increase days to cover today, rather than leave it at yesterday

	// work out which month we're in
	// get all days in months prior to this one
	uint32_t year = __KLIB__SystemTimeGetYear();
	uint32_t count_days_low = 1;
	uint32_t count_days_high = days_in_month[0];
	uint32_t i;
	for(i = 0; i < 12; i++)
	{
		// see if it falls between count days low and high
		if(days >= count_days_low && days <= count_days_high)
		{
			break;
		}

		// is not, increase counts
		count_days_low = count_days_high + 1;
		count_days_high += days_in_month[i + 1];

		// check if high month is february and is a leap year
		if(i == 0 && (year % 4) == 0 && year != 1900 && year != 2100)
		{
			count_days_high++;
		}
	}

	// i gives the month - 1
	return ++i;
}


uint32_t	__KLIB__SystemTimeGetDay(void)
{
	// get remaining days of year
	uint32_t years, days, millis;
	GetYearsSinceEpoc(&years, &days, &millis);
	days++;	// increase days to cover today, rather than leave it at yesterday

	// need to get current year and current month
	uint32_t year = __KLIB__SystemTimeGetYear();
	uint32_t month = __KLIB__SystemTimeGetMonth();

	// now need to add all the days for the months prior to this one since january of this year
	uint32_t i, c_days;
	for(i = 0, c_days = 0; i < (month - 1); i++)
	{
		c_days += days_in_month[i];

		// add a day if february and a leap year
		if(i == 1)
		{
			if((year % 4 == 0) && (year != 2100) && (year != 1900))
			{
				c_days += 1;
			}
		}
	}

	return (days - c_days);
}


uint32_t	__KLIB__SystemTimeGetHour(void)
{
	// get the remaining millis today
	uint32_t days, millis;
	GetDaysSinceEpoc(&days, &millis);

	// divide it by millis per hour to get hour
	return millis / (1000 * 60 * 60);
}


uint32_t	__KLIB__SystemTimeGetMinute(void)
{
	// get the remaining millis today
	uint32_t days, millis;
	GetDaysSinceEpoc(&days, &millis);

	// mod it by millis per hour
	uint32_t h = millis % (1000 * 60 * 60);

	// divide by millis per minute to get minutes
	return h / (1000 * 60);
}


uint32_t	__KLIB__SystemTimeGetSecond(void)
{
	// get the remaining millis today
	uint32_t days, millis;
	GetDaysSinceEpoc(&days, &millis);

	// mod it by millis per minute
	uint32_t m = millis % (1000 * 60);

	// then divide by millis per second to get seconds
	return m / 1000;
}


uint32_t	__KLIB__SystemTimeGetMSecond(void)
{
	// get the remaining millis today
	uint32_t days, millis;
	GetDaysSinceEpoc(&days, &millis);

	// mod it by millis per second
	return millis % 1000;
}


uint64_t 	__KLIB__SystemTimeGetSecondSincEpoc(void)
{
	return GetSystemMillis() / 1000;
}

uint64_t	__KLIB__SystemTimeGetMSecondSinceEpoc(void)
{
	return GetSystemMillis();
}
















